//
// Created by denglibin on 2021/4/7.
//

#include "glfw_window.h"
#include <iostream>

std::map<GLFWwindow*, Window*>  Window::m;

Window* Window :: findWindow(GLFWwindow* glfW){
    auto it  = Window::m.find(glfW);
    if (it == Window::m.end()){
        return nullptr;
    }
    return it->second;
}
Window::Window() {
    init();
}
Window::Window(int width, int height, std::string &title){
    this->width = width;
    this->height = height;
    this->title = title;
    init();
}
Window::~Window() {
    delete camera;
}
void Window::init() {
    camera = new Camera();
    //初始化GLFW
    glfwInit();
    //将主版本号(Major)和次版本号(Minor)都设为3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //告诉GLFW我们使用的是核心模式(Core-profile)
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow *window = glfwCreateWindow(this->width, this->height, title.c_str(), nullptr, nullptr);
    if (window == nullptr)
    {
        std::cout << "Failed to create GLFW window\n" <<std::endl;
        glfwTerminate();
        exit(1);
    }
    glfwMakeContextCurrent(window);

    //初始化GLAD库 任何的OpenGL接口调用都必须在初始化GLAD库后才可以正常访问
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        exit(1);
    }

    // 注册窗口变化监听
    glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
    //键盘监听
    glfwSetKeyCallback(window, keyCallback);
    //调用这个函数之后，无论我们怎么去移动鼠标，光标都不会显示了，它也不会离开窗口。对于FPS摄像机系统来说非常完美。
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    //注册鼠标回调函数
    glfwSetCursorPosCallback(window, mouseCallback);
    //注册鼠标滚轮的回调函数
    glfwSetScrollCallback(window, scrollCallback);
    //开启深度测试
    glEnable(GL_DEPTH_TEST);

    // 设置视口,告诉opengl渲染窗口的尺寸大小
    glViewport(0, 0, this->width, this->height);
    this->glfwWindow = window;
    m.insert(std::make_pair(window ,this));
}

/**
 * 键盘按下事件
 */
void Window::keyPressed(int key) {
    switch (key)
    {
        case GLFW_KEY_ESCAPE:
            this->close();
            break;
        case GLFW_KEY_W:
            this->camera->moveFront();
            break;
        case GLFW_KEY_S:
            this->camera->moveBack();
            break;
        case GLFW_KEY_A:
            this->camera->moveLeft();
            break;
        case GLFW_KEY_D:
            this->camera->moveRight();
            break;
        case GLFW_KEY_UP:
            this->camera->moveUp();
            break;
        case GLFW_KEY_DOWN:
            this->camera->moveDown();
            break;
        default:
            break;
    }

}

void Window::paint() {
    //设置清除颜色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    //使用上面设置的颜色清空屏幕
    // glClear(GL_COLOR_BUFFER_BIT);
    //因为使用了深度测试，我们也想要在每次渲染迭代之前清除深度缓冲（否则前一帧的深度信息仍然保存在缓冲中）。就像清除颜色缓冲一样
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void Window::show() {
    while (!glfwWindowShouldClose(this->glfwWindow)){
        paint();
        //交换颜色缓冲区
        glfwSwapBuffers(this->glfwWindow);
        //检查事件
        glfwPollEvents();
    }
    clean();
}

Camera* Window::getCamera() {
    return camera;
}

void Window::close() {
    glfwSetWindowShouldClose(this->glfwWindow, 1);
}

void Window::clean(){
    glfwTerminate();
}

/**
     * 窗口大小改变的回调
     * @param window
     * @param w
     * @param h
     */
void framebufferSizeCallback(GLFWwindow *window, int w, int h){
    //当用户改变窗口的大小的时候，视口也应该被调整
    glViewport(0, 0, w, h);
    Window* win = Window::findWindow(window);
    if(win != nullptr){
       win->width = w;
       win->height = h;
    }
}

void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods){

    Window* w = Window::findWindow(window);
    if(w != nullptr){
        w->keyPressed(key);
    }
}
/**
 * 鼠标回调
 * @param win
 * @param x
 * @param y
 * @return
 */

void mouseCallback(GLFWwindow* win, double x, double y){
    Window* w = Window::findWindow(win);
    if(w != nullptr){
        w->camera->mouseDirection(x, y);
    }
}
/**
 * 滚轮回调
 * @param win
 * @param x_offset
 * @param y_offset
 */
void scrollCallback(GLFWwindow* win, double x_offset, double y_offset){
    Window* w = Window::findWindow(win);
    if(w != nullptr){
        w->camera-> scrollFov( x_offset,  y_offset);
    }
}